﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LCQ.QueryBuilder
{
    public class WhereStatement : List<List<WhereClause>>
    {
        public void Add(WhereClause clause)
        {
            this.Add(clause, 1);
        }

        public void Add(WhereClause clause, int level)
        {
            this.AddWhereClauseToLevel(clause, level);
        }

        public WhereClause Add(Enum field, Comparison @operator, object compareValue)
        {
            return this.Add(field.ToString(), @operator, compareValue, 1);
        }

        public WhereClause Add(string field, Comparison @operator, object compareValue)
        {
            return this.Add(field, @operator, compareValue, 1);
        }

        public WhereClause Add(string field, Comparison @operator, object compareValue, int level)
        {
            WhereClause clause = new WhereClause(field, @operator, compareValue);
            this.AddWhereClauseToLevel(clause, level);
            return clause;
        }

        private void AddWhereClause(WhereClause clause)
        {
            this.AddWhereClauseToLevel(clause, 1);
        }

        private void AddWhereClauseToLevel(WhereClause clause, int level)
        {
            this.AssertLevelExistance(level);
            base[level - 1].Add(clause);
        }

        private void AssertLevelExistance(int level)
        {
            if (base.Count < (level - 1))
            {
                throw new Exception(string.Concat(new object[] { "Level ", level, " not allowed because level ", level - 1, " does not exist." }));
            }
            if (base.Count < level)
            {
                base.Add(new List<WhereClause>());
            }
        }

        public string BuildWhereStatement()
        {
            DbCommand usedDbCommand = null;
            return this.BuildWhereStatement(false, ref usedDbCommand);
        }

        public string BuildWhereStatement(bool useCommandObject, ref DbCommand usedDbCommand)
        {
            string str = "";
            foreach (List<WhereClause> list in this)
            {
                string str2 = "";
                foreach (WhereClause clause in list)
                {
                    string str3 = "";
                    if (useCommandObject)
                    {
                        string str4 = string.Format("@p{0}_{1}", usedDbCommand.Parameters.Count + 1, clause.FieldName.Replace('.', '_'));
                        DbParameter parameter = usedDbCommand.CreateParameter();
                        parameter.ParameterName = str4;
                        parameter.Value = clause.Value;
                        usedDbCommand.Parameters.Add(parameter);
                        str3 = str3 + CreateComparisonClause(clause.FieldName, clause.ComparisonOperator, new SqlLiteral(str4));
                    }
                    else
                    {
                        str3 = CreateComparisonClause(clause.FieldName, clause.ComparisonOperator, clause.Value);
                    }
                    foreach (WhereClause.SubClause clause2 in clause.SubClauses)
                    {
                        switch (clause2.LogicOperator)
                        {
                            case LogicOperator.And:
                                str3 = str3 + " AND ";
                                break;

                            case LogicOperator.Or:
                                str3 = str3 + " OR ";
                                break;
                        }
                        if (useCommandObject)
                        {
                            string str5 = string.Format("@p{0}_{1}", usedDbCommand.Parameters.Count + 1, clause.FieldName.Replace('.', '_'));
                            DbParameter parameter2 = usedDbCommand.CreateParameter();
                            parameter2.ParameterName = str5;
                            parameter2.Value = clause2.Value;
                            usedDbCommand.Parameters.Add(parameter2);
                            str3 = str3 + CreateComparisonClause(clause.FieldName, clause2.ComparisonOperator, new SqlLiteral(str5));
                        }
                        else
                        {
                            str3 = str3 + CreateComparisonClause(clause.FieldName, clause2.ComparisonOperator, clause2.Value);
                        }
                    }
                    str2 = str2 + "(" + str3 + ") AND ";
                }
                str2 = str2.Substring(0, str2.Length - 5);
                if (list.Count > 1)
                {
                    str = str + " (" + str2 + ") ";
                }
                else
                {
                    str = str + " " + str2 + " ";
                }
                str = str + " OR";
            }
            return str.Substring(0, str.Length - 2);
        }

        public static WhereStatement CombineStatements(WhereStatement statement1, WhereStatement statement2)
        {
            WhereStatement statement = Copy(statement1);
            for (int i = 0; i < statement2.ClauseLevels; i++)
            {
                List<WhereClause> list = statement2[i];
                foreach (WhereClause clause in list)
                {
                    for (int j = 0; j < statement.ClauseLevels; j++)
                    {
                        statement.AddWhereClauseToLevel(clause, j);
                    }
                }
            }
            return statement;
        }

        public static WhereStatement Copy(WhereStatement statement)
        {
            WhereStatement statement2 = new WhereStatement();
            int num = 0;
            using (List<List<WhereClause>>.Enumerator enumerator = statement.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    List<WhereClause> current = enumerator.Current;
                    num++;
                    statement2.Add(new List<WhereClause>());
                    foreach (WhereClause clause in statement[num - 1])
                    {
                        WhereClause item = new WhereClause(clause.FieldName, clause.ComparisonOperator, clause.Value);
                        foreach (WhereClause.SubClause clause3 in clause.SubClauses)
                        {
                            WhereClause.SubClause clause4 = new WhereClause.SubClause(clause3.LogicOperator, clause3.ComparisonOperator, clause3.Value);
                            item.SubClauses.Add(clause4);
                        }
                        statement2[num - 1].Add(item);
                    }
                }
            }
            return statement2;
        }

        internal static string CreateComparisonClause(string fieldName, Comparison comparisonOperator, object value)
        {
            string str = "";
            if ((value != null) && (value != DBNull.Value))
            {
                switch (comparisonOperator)
                {
                    case Comparison.Equals:
                        return (fieldName + " = " + FormatSQLValue(value));

                    case Comparison.NotEquals:
                        return (fieldName + " <> " + FormatSQLValue(value));

                    case Comparison.Like:
                        return (fieldName + " LIKE " + FormatSQLValue(value));

                    case Comparison.NotLike:
                        return ("NOT " + fieldName + " LIKE " + FormatSQLValue(value));

                    case Comparison.GreaterThan:
                        return (fieldName + " > " + FormatSQLValue(value));

                    case Comparison.GreaterOrEquals:
                        return (fieldName + " >= " + FormatSQLValue(value));

                    case Comparison.LessThan:
                        return (fieldName + " < " + FormatSQLValue(value));

                    case Comparison.LessOrEquals:
                        return (fieldName + " <= " + FormatSQLValue(value));

                    case Comparison.In:
                        return (fieldName + " IN (" + FormatSQLValue(value) + ")");

                    case Comparison.InString:
                        return string.Concat(new object[] { fieldName, " IN (", value, ")" });
                }
                return str;
            }
            if ((comparisonOperator != Comparison.Equals) && (comparisonOperator != Comparison.NotEquals))
            {
                throw new Exception("Cannot use comparison operator " + comparisonOperator.ToString() + " for NULL values.");
            }
            switch (comparisonOperator)
            {
                case Comparison.Equals:
                    return (fieldName + " IS NULL");

                case Comparison.NotEquals:
                    return ("NOT " + fieldName + " IS NULL");
            }
            return str;
        }

        internal static string FormatSQLValue(object someValue)
        {
            if (someValue == null)
            {
                return "NULL";
            }
            switch (someValue.GetType().Name)
            {
                case "String":
                    return ("'" + ((string)someValue).Replace("'", "''") + "'");

                case "DateTime":
                    {
                        DateTime time = (DateTime)someValue;
                        return ("'" + time.ToString("yyyy/MM/dd hh:mm:ss") + "'");
                    }
                case "DBNull":
                    return "NULL";

                case "Boolean":
                    return (((bool)someValue) ? "1" : "0");

                case "SqlLiteral":
                    return ((SqlLiteral)someValue).Value;
            }
            return someValue.ToString();
        }

        public int ClauseLevels
        {
            get
            {
                return base.Count;
            }
        }
    }
}
